package com.foo.common.base.utils;

import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.joda.time.DateTime;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.Paths;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

import static com.foo.common.base.utils.FooUtils.*;

public class VcsHelper {
    private final static Logger LOGGER = LoggerFactory.getLogger(VcsHelper.class);
    private final static String modify = "modify";
    private final static String add = "add";
    private final static String delete = "delete";
    private final static String vcs = "git";
    private final static String vcsPath = "";
    private final static String dtsDir = "";
    private static Collection<String> vcsChangedFiles = Sets.newTreeSet();
    private static String revisionNumber = "";
    private String beyongCompareExePath = "yourPath\\BeyondCompare4\\BCompare.exe";
    private String excelNamePostfix = "test.xlsx";
    private String wordNamePostfix = "test.docx";
    private String vcsDiffCommandPath = "resources\\vcsDiff.txt";
    private String workingDir = "\\vcsTmp\\";
    private String workingSourceDir = workingDir + "source";
    private String workingTargetDir = workingDir + "target";
    private String workingReportDir = workingDir + "report";
    private String dtsTemplateDir = dtsDir + "DTS-template";
    private String dtsDesc = "";
    private String dtsNumber = "";
    private String dtsWorkingDir = dtsDir + dtsNumber + "\\";

    @BeforeClass
    public static void oneTimeSetUp() throws Exception {
        LOGGER.info("oneTimeSetUp now.");
        Preconditions.checkArgument(Objects.equals(vcs, "svn") || Objects.equals(vcs, "git"));
        vcsChangedFiles = FooUtils.readLines(FooUtils.getClassPathResourceFile("vcsChangedFiles.txt"));
        LOGGER.info("reading from file:{} and result as:{}", "vcsChangedFiles.txt", vcsChangedFiles);
        if (Objects.equals(vcs, "git")) {
            revisionNumber = getSecondLastRevision();
            LOGGER.info("git and change revisionNumber to:{}", revisionNumber);
        }
    }

    public static String getSecondLastRevision() throws Exception {
        String result = FooUtils.executeWindowsCommand("cd " + vcsPath
                + " && git log --pretty=format:\"%H$%cn$%cd$%s\" -2 --date=iso");
        List<String> logs = Splitter.on("\n").splitToList(result);
        Preconditions.checkArgument(logs.size() >= 2);
        List<String> secondLastRevisions = Splitter.on("$").limit(2).splitToList(logs.get(1));
        Preconditions.checkArgument(secondLastRevisions.size() >= 2);
        String secondLastRevision = secondLastRevisions.get(0);
        Preconditions.checkArgument(secondLastRevision.length() == 40);
        return secondLastRevision;
    }

    @Test
    public void generateDts() throws Exception {
        generateDtsDocs();
        updateDtsDocs();
        generateCompareReport();
        generateAutoFillScript();
    }

    @Test
    public void updateDtsDocs() throws Exception {
        File tmplExcelFile = (new File(dtsWorkingDir, excelNamePostfix));
        InputStream inputStream = null;
        FileOutputStream ouputFile = null;
        try {
            inputStream = new FileInputStream(tmplExcelFile);
            Workbook wb = WorkbookFactory.create(inputStream);
            Sheet sheet = wb.getSheet("tmpl");
            Row modifiedRow = sheet.getRow(3);
            Cell cell = modifiedRow.createCell(getExcelColumn("a"));
            cell.setCellValue(dtsDesc);
            cell = modifiedRow.createCell(getExcelColumn("b"));
            cell.setCellValue(dtsNumber);
            cell = modifiedRow.createCell(getExcelColumn("e"));
            cell.setCellValue(FooUtils.toDateFromYear2Day(new DateTime()) + "");
            LOGGER.info("{}", getExcelCellValue(cell));
            ouputFile = new FileOutputStream(tmplExcelFile);
            wb.write(ouputFile);
        } finally {
            FooUtils.silentClose(inputStream, ouputFile);
        }
        File tmpWordFile = new File(dtsWorkingDir, wordNamePostfix);
        XWPFDocument doc = new XWPFDocument(new FileInputStream(tmpWordFile));
        List<XWPFParagraph> paras = doc.getParagraphs();
        String paragraphText;
        String text;
        try {
            for (XWPFParagraph para : paras) {
                paragraphText = para.getParagraphText().trim();
                LOGGER.trace("para text is:{}", paragraphText);
                for (XWPFRun xwpfRun : para.getRuns()) {
                    text = Strings.nullToEmpty(xwpfRun.getText(xwpfRun.getTextPosition()));
                    LOGGER.trace("analyze for:{}", text);
                    if (text.contains("dtsDesc")) {
                        xwpfRun.setText(dtsDesc, 0);
                        xwpfRun.addBreak();
                    } else if (text.contains("vcsChangedFiles")) {
                        xwpfRun.setText("", 0);
                        for (String changedLineStr : vcsChangedFiles) {
                            xwpfRun.addBreak();
                            xwpfRun.setText(changedLineStr);
                        }
                    } else {
                        continue;
                    }
                }
            }
            ouputFile = new FileOutputStream(tmpWordFile);
            doc.write(ouputFile);
        } finally {
            silentClose(ouputFile);
        }
    }


    @Test
    public void generateAutoFillScript() throws Exception {
        String autoFillScript = FooUtils.readFileToString(FooUtils.getClassPathResourceFile("dtsAutoFillForm.txt"));
        String changedFilesStr = "";
        String result = FooUtils.generateDataWithMustache(ImmutableMap.<String, Object>of("changedFiles", changedFilesStr), autoFillScript);
        LOGGER.info("autoFillScript as following: \n\n\n{} ", result);
    }

    @Test
    public void generateDtsDocs() throws IOException {
        LOGGER.info("clean working dir:{}", dtsWorkingDir);
        FooUtils.forceMkdir(dtsWorkingDir);
        FooUtils.cleanDirectory(new File(dtsWorkingDir));
        File dtsTmplFile;
        File dtsTargetFile;
        dtsTmplFile = new File(dtsTemplateDir, excelNamePostfix);
        excelNamePostfix = dtsNumber + excelNamePostfix;
        dtsTargetFile = new File(dtsWorkingDir, excelNamePostfix);
        FooUtils.copyFile(dtsTmplFile, dtsTargetFile);
        dtsTmplFile = new File(dtsTemplateDir, wordNamePostfix);
        wordNamePostfix = dtsNumber + wordNamePostfix;
        dtsTargetFile = new File(dtsWorkingDir, wordNamePostfix);
        FooUtils.copyFile(dtsTmplFile, dtsTargetFile);
        String namePostfix = "test.doc";
        dtsTargetFile = new File(dtsWorkingDir, dtsNumber + namePostfix);
        FooUtils.copyFile(new File(dtsTemplateDir, namePostfix), dtsTargetFile);
    }

    @Test
    public void generateCompareReport() throws IOException {
        Preconditions.checkArgument(Paths.get(dtsWorkingDir).toFile().isDirectory());
        LOGGER.info("clean working dir:{}", workingDir);
        FooUtils.forceMkdir(workingDir);
        FooUtils.cleanDirectory(new File(workingDir));
        FooUtils.forceMkdir(workingSourceDir);
        FooUtils.forceMkdir(workingTargetDir);
        FooUtils.forceMkdir(workingReportDir);
        File sourceFile;
        File targetFile;
        String fileName;
        VcsVersion targetVcsVerion = VcsVersion.specified;
        if (isSvn()) {
            targetVcsVerion.setRevisionNumber(String.valueOf(Long.parseLong(revisionNumber) - 1));
        } else {
            targetVcsVerion.setRevisionNumber(revisionNumber);
        }
        for (String file : vcsChangedFiles) {
            fileName = FooUtils.getFileName(file);
            sourceFile = new File(workingSourceDir, fileName);
            targetFile = new File(workingTargetDir, fileName);
            FooUtils.writeStringToFile(sourceFile, getFileDataFromVcsVersion(file, targetVcsVerion));
            FooUtils.writeStringToFile(targetFile, getFileDataFromVcsVersion(file, VcsVersion.local));
        }
        FooUtils.executeWindowsCommand("" + beyongCompareExePath + " @" + vcsDiffCommandPath + "");
        ZipHelper.instance.compressDirToZip(workingReportDir, dtsWorkingDir + "Diff_代码对比.rar");
    }

    private String getFileDataFromVcsVersion(String filePath, VcsVersion vcsVersion) throws IOException {
        LOGGER.info("getFileDataFromVcsVersion for path:{} and vcsVersion:{}", filePath, vcsVersion);
        File file = Paths.get(filePath).toFile();
        Preconditions.checkArgument(file.isFile());
        if (vcsVersion == VcsVersion.local) {
            return FooUtils.readFileToString(file);
        } else if (vcsVersion == VcsVersion.latest) {
            throw new UnsupportedOperationException();
        } else if (vcsVersion == VcsVersion.specified) {
            if (isSvn()) {
                return FooUtils.executeWindowsCommand("svn cat -r " + vcsVersion.getRevisionNumber() + " "
                        + filePath + "");
            }
            filePath = filePath.replace(vcsPath, "").replaceAll("\\\\", "/");
            filePath = filePath.substring(1, filePath.length());
            return FooUtils.executeWindowsCommand("cd " + vcsPath + " && git cat-file -p "
                    + vcsVersion.getRevisionNumber() + ":" + filePath + "");
        } else {
            throw new UnsupportedOperationException();
        }
    }

    private boolean isSvn() {
        return Objects.equals(vcs, "svn");
    }

    private enum VcsVersion {
        local(""), latest(""), specified;

        private String revisionNumber = "";

        VcsVersion(String revisionNumber) {
            this.revisionNumber = revisionNumber;
        }

        VcsVersion() {
        }

        public String getRevisionNumber() {
            return revisionNumber;
        }

        public void setRevisionNumber(String revisionNumber) {
            this.revisionNumber = revisionNumber;
        }

        @Override
        public String toString() {
            return MoreObjects.toStringHelper(this).add("revisionNumber", revisionNumber).toString();
        }
    }
}
